﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Tries to get an outline contour of a shape
    /// </summary>
    public class OutlineContours : BaseForeBackgroundColorFilter
    {
        protected Color m_ContourColor = Color.Blue;
        protected Color m_ContourToleranceColor = Definitions.White;
        protected int m_nRTolerance = 52;
        protected int m_nGTolerance = 52;
        protected int m_nBTolerance = 52;
        protected int m_nTolerance = 127;
        protected bool m_bDefaultMethod = true;
        protected int m_nAllowedErrorCount = 0;

        /// <summary>
        /// default constructor
        /// </summary>
        public OutlineContours()
        {
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_outlineContours"></param>
        internal OutlineContours(OutlineContours _outlineContours)
        {
            m_bDefaultMethod = _outlineContours.m_bDefaultMethod;
            m_nTolerance = _outlineContours.m_nTolerance;
            m_ContourColor = _outlineContours.m_ContourColor;
            m_ContourToleranceColor = _outlineContours.m_ContourToleranceColor;
        }

        /// <summary>
        /// Use default mode
        /// </summary>
        public bool UseDefaultMode
        {
            get { return m_bDefaultMethod; }
            set { m_bDefaultMethod = value; }
        }

        /// <summary>
        /// Gets or sets tolerance for non default method
        /// </summary>
        public int Tolerance
        {
            get { return m_nTolerance; }
            set { m_nTolerance = value; }
        }


        //optimize: put both loops into one
        /// <summary>
        /// Executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            int y = 0;
            int x = 0;

            if (m_bDefaultMethod)
            {
                DefaultMethod(bitmap, nHeight, nWidth, ref y, ref x);
            }
            else
            {
                AlternativeMethod(_bitmap, bitmap, nHeight, nWidth, ref y, ref x);
            }
            return bitmap;
        }

        /// <summary>
        /// Alternative Method
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="bitmap"></param>
        /// <param name="nHeight"></param>
        /// <param name="nWidth"></param>
        /// <param name="y"></param>
        /// <param name="x"></param>
        private void AlternativeMethod(UnsafeBitmap _bitmap, UnsafeBitmap bitmap, int nHeight, int nWidth, ref int y, ref int x)
        {
            int nOffset = (int)m_fOffset;
            Color color1 = Color.Transparent;
            Color color2 = Color.Transparent;
            int nErrorCount = 0;

            UnsafeBitmap bitmapCloned = (UnsafeBitmap)_bitmap.Clone();
            int nXLeft = 0;
            int nXRight = 0;
            for (y = 0; y < nHeight; y++)
            {
                for (x = nOffset; x < nWidth - 1; x++)
                {
                    color1 = bitmapCloned.GetPixel(x, y);
                    color2 = bitmapCloned.GetPixel(x + 1, y);
                    if (ColorFunctions.DistanceSquared(color1, color2) > m_nTolerance)
                    {
                        if (m_nAllowedErrorCount > 0 && nErrorCount < m_nAllowedErrorCount)
                        {
                            nErrorCount++;
                            continue;
                        }
                        nErrorCount = 0;
                        _bitmap.SetPixel(x, y, m_ContourColor);
                        nXLeft = x;
                        break;
                    }
                    else
                    {
                        _bitmap.SetPixel(x, y, m_ColorBackground);
                    }
                }
                for (x = nWidth - 1 - nOffset; x > -1; x--)
                {
                    color1 = bitmapCloned.GetPixel(x, y);
                    color2 = bitmapCloned.GetPixel(x + 1, y);
                    if (ColorFunctions.DistanceSquared(color1, color2) > m_nTolerance)
                    {
                        if (m_nAllowedErrorCount > 0 && nErrorCount < m_nAllowedErrorCount)
                        {
                            nErrorCount++;
                            continue;
                        }
                        nErrorCount = 0;
                        _bitmap.SetPixel(x, y, m_ContourColor);
                        nXRight = x;
                        break;
                    }
                    else
                    {
                        _bitmap.SetPixel(x, y, m_ColorBackground);
                    }
                }
                if (nXLeft != -1 && nXRight != -1)
                {
                    for (x = nXLeft + 1; x < nXRight - 1; x++)
                    {
                        _bitmap.SetPixel(x, y, m_ColorBackground);
                    }
                }
                nXLeft = -1;
                nXRight = -1;
            }
        }

        /// <summary>
        /// Default Method
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="nHeight"></param>
        /// <param name="nWidth"></param>
        /// <param name="y"></param>
        /// <param name="x"></param>
        private void DefaultMethod(UnsafeBitmap bitmap, int nHeight, int nWidth, ref int y, ref int x)
        {
            Color color = Color.Transparent;
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color = bitmap.GetPixel(x, y);
                    if (ColorFunctions.IsInColorTolerance(color, m_ContourToleranceColor, m_nRTolerance, m_nGTolerance, m_nBTolerance))
                    {
                        bitmap.SetPixel(x, y, m_ColorForeground);
                    }
                }
            }

            int nStartX = 0;
            int nEndX = 0;
            List<Range> aPoint = new List<Range>();
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color = bitmap.GetPixel(x, y);
                    if (color != m_ContourToleranceColor)
                    {
                        nStartX = x;
                    }
                }
                for (x = nWidth - 1; x > -1; x--)
                {
                    color = bitmap.GetPixel(x, y);
                    if (color != m_ContourToleranceColor)
                    {
                        nEndX = x;
                    }
                }
                aPoint.Add(new Range(nStartX, nEndX, y, m_ContourColor));
            }

            bitmap.Clear();
            int i = 0;
            foreach (Range range in aPoint)
            {
                bitmap.SetPixel(range.StartX, range.Y, m_ContourColor);
                bitmap.SetPixel(range.EndX, range.Y, m_ContourColor);
                for (i = range.StartX + 1; i < range.EndX - 1; i++)
                {
                    bitmap.SetPixel(i, range.Y, m_ColorBackground);
                }
            }
        }

        /// <summary>
        /// Gets or sets contour color
        /// </summary>
        public Color ContourColor
        {
            get { return m_ContourColor; }
            set { m_ContourColor = value; }
        }

        /// <summary>
        /// Gets or sets Contour Tolerance Color
        /// </summary>
        public Color ContourToleranceColor
        {
            get { return m_ContourToleranceColor; }
            set { m_ContourToleranceColor = value; }
        }

        /// <summary>
        /// Gets or sets RTolerance
        /// </summary>
        public int RTolerance
        {
            get { return m_nRTolerance; }
            set { m_nRTolerance = value; }
        }

        /// <summary>
        /// Gets or sets GTolerance
        /// </summary>
        public int GTolerance
        {
            get { return m_nGTolerance; }
            set { m_nGTolerance = value; }
        }

        /// <summary>
        /// Gets or sets BTolerance
        /// </summary>
        public int BTolerance
        {
            get { return m_nBTolerance; }
            set { m_nBTolerance = value; }
        }

        /// <summary>
        /// create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new OutlineContours(this);
        }
    }
}
